package hypergraph;

import java.awt.Dimension;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JFrame;


import org.apache.commons.collections15.list.SetUniqueList;
import org.apache.commons.collections15.map.ListOrderedMap;
import org.apache.commons.collections15.set.ListOrderedSet;

import bangor.aiia.jge.evolution.Genesis;
import bangor.aiia.jge.population.InvalidPhenotypeException;

import edu.uci.ics.jung.algorithms.layout.CircleLayout;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.FRLayout2;
import edu.uci.ics.jung.algorithms.layout.ISOMLayout;
import edu.uci.ics.jung.algorithms.layout.KKLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.SpringLayout;
import edu.uci.ics.jung.algorithms.scoring.DistanceCentralityScorer;
import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.Hypergraph;
import edu.uci.ics.jung.graph.SetHypergraph;
import edu.uci.ics.jung.graph.SparseMultigraph;
import edu.uci.ics.jung.graph.util.EdgeType;
import edu.uci.ics.jung.io.GraphMLWriter;
import edu.uci.ics.jung.visualization.BasicVisualizationServer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import evolution.CodonString;

public class HyperGraph extends OrderedSetHypergraph<Node, HyperEdge> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	//ListOrderedMap<Node, ListOrderedSet<HyperEdge>> vertices;
	//ListOrderedMap<HyperEdge, ListOrderedSet<Node>> edges;
	
	public HyperGraph() {

	}
	
	public HyperGraph(String str){
		this();
		parse(str);
	}
	
	public void print(){
		//System.out.println("nodes");
		for(Node n : getVertices())
			n.print();
		//System.out.println("hyperEdges");
		for(HyperEdge e : getEdges()){
			e.print();
			for(Node in : getIncidentVertices(e))
				in.print();
		}
		
	}

	
	public Node addNode(Node n){
		if(addVertex(n))
			return n;
		else 
			return null;
	}
	public List<Node> addNodes(List<Node> nodes){
		for(int i = 0; i < nodes.size(); i++)
			nodes.set(i, addNode(nodes.get(i)));
		return nodes;
	}
	

	public List<Node> getNodes(){
		return ((ListOrderedMap<Node, List<HyperEdge>>)vertices).asList();
		//return new ArrayList<Node>(getVertices());
	}
	public List<HyperEdge> getHyperEdges(){
		return ((ListOrderedMap<HyperEdge, List<Node>>)edges).asList();
	}
	public HyperEdge addHyperEdge(HyperEdge e, List<Node> nodes){
		addEdge(e, nodes);
		return e;
	}
	
	public Node replaceNode(Node n, Node r){

		addVertex(r);
		SetUniqueList<HyperEdge> iedges = 
			SetUniqueList.decorate(new ArrayList<HyperEdge>(getIncidentEdges(n))); 
		for(HyperEdge e : iedges){
			List<Node> incident = edges.get(e);
			incident.set(incident.indexOf(n) , r);
		}
		vertices.put(r, iedges);
		removeVertex(n);
		return r;
	}
	
	public HyperEdge replaceEdge(HyperEdge e, HyperEdge r){
		List<Node> incident = edges.get(e);
		removeEdge(e);
		addEdge(r, incident);
		return r;
	}

    /**
     * @see Hypergraph#getEdgeType(Object)
     */
    public EdgeType getEdgeType(HyperEdge edge)
    {
        if (containsEdge(edge))
            return EdgeType.DIRECTED;
        else
            return null;
    }
	
	public void parse(String str){
		Pattern p = Pattern.compile("([A-Z][0-9]+)");
		Matcher edges = p.matcher(str);
		
		HashMap<Integer, Node> nodes = new HashMap<Integer, Node>();
		for(int m = 0; m < 4; m++){
			Node n = new Node();
			nodes.put(m, n);
		}

		while(edges.find()){
			//System.out.println("edges: " + edges.groupCount());
			for(int i = 0; i < edges.groupCount(); i++){
				String edge = edges.group(i);
				//System.out.println(edge);
				char type = edge.charAt(0);
				ArrayList<Node> incident = new ArrayList<Node>();
				for(int k = 1; k < edge.length(); k++){
					Integer node = Integer.parseInt(edge.substring(k, k+1));
					
					/*if(!nodes.containsKey(node)){
						Node n = new Node();
						nodes.put(node, n);
						this.addVertex(n);
					}*/
					incident.add(nodes.get(node));
				}
				//System.out.println("char" + type + " incident size" + incident.size());
				this.addEdge(new HyperEdge(type), incident);
			}
		}
		//Collections.sort(this.getVertices());
		
	}
	
	public ArrayList<HyperEdge> listNonTerminals(){
		ArrayList<HyperEdge> nt = new ArrayList<HyperEdge>();
		for(HyperEdge e : getEdges()){
			if(e.getLabel() != 'Z'){
				nt.add(e);
			}
		}
		return nt;
	}
	
	public DirectedSparseMultigraph<Node, HyperEdge> toGraph(){
		DirectedSparseMultigraph<Node, HyperEdge> g = new DirectedSparseMultigraph<Node, HyperEdge>();
		for ( Node n : this.getVertices() )
			if(getIncidentEdges(n).size() > 0)
				g.addVertex(n);
		int i = 0;
		for ( HyperEdge e : this.getEdges() ) {
			//System.out.println("i"+i);
			Collection<Node> incident = this.getIncidentVertices(e);
			//if(incident.size() > 0)
				g.addEdge(e, incident);
			i++;
		}
		
		
		return g;
	}
	
	public HyperGraph clone(){
		HyperGraph c = new HyperGraph();
		c.add(this);
		List<Node> nodes = c.getNodes();
		//int size = nodes.size();
		for(int i = nodes.size()-1; i >= 0; i--){
			c.replaceNode(nodes.get(i), nodes.get(i).clone());

		}
		List<HyperEdge> edges = c.getHyperEdges();
		//size = edges.size();
		for(int i = edges.size()-1; i >= 0; i--){
			c.replaceEdge(edges.get(i), edges.get(i).clone());
		}

		return c;
	}

	public void add(HyperGraph g){
		// copy the vertices in
		for ( Node n : g.getVertices() )
			if(!this.containsVertex(n))
				this.addVertex(n);
		int i = 0;
		for ( HyperEdge e : g.getEdges() ) {
			if(!this.containsEdge(e)){
				this.addEdge(e, g.getIncidentVertices(e));
			}
		}
	}
	
	
	
	public void replace(HyperEdge e, HyperGraph rep){
		ArrayList<Node> externalNodes = new ArrayList<Node>(this.getIncidentVertices(e));
		//ArrayList<Node> toRemove = new ArrayList<Node>();
		this.removeEdge(e);
		int size = rep.getVertexCount();
		
		List<Node> vertices = rep.getNodes();
		//System.out.println(
	//			"size"+size+"exernal size"+externalNodes.size());
		for(int i = 0; i < size; i++){
			//System.out.println("i"+i+"size"+size+"exernal size"+externalNodes.size());
			Node r = vertices.get(i);
			if(i < externalNodes.size()){			
				//System.out.println("i"+i+"size"+size);
				rep.replaceNode(r, externalNodes.get(i));
			}
		}

		
		add(rep);
		
	}


	
	public void printML(){
		GraphMLWriter<Node,HyperEdge> writer = new GraphMLWriter<Node,HyperEdge>();
		Writer out = new PrintWriter(System.out);
		
		try {
			writer.save(this, out);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
